Skip to content

Method: createNativeQuery(String, Class)

1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: * <p>
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.sessions;
16:
17: import cz.cvut.kbss.jopa.adapters.IndirectCollection;
18: import cz.cvut.kbss.jopa.exceptions.OWLEntityExistsException;
19: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
20: import cz.cvut.kbss.jopa.model.AbstractEntityManager;
21: import cz.cvut.kbss.jopa.model.EntityManagerImpl.State;
22: import cz.cvut.kbss.jopa.model.MetamodelImpl;
23: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
24: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
25: import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
26: import cz.cvut.kbss.jopa.model.query.Query;
27: import cz.cvut.kbss.jopa.model.query.TypedQuery;
28: import cz.cvut.kbss.jopa.query.NamedQueryManager;
29: import cz.cvut.kbss.jopa.query.sparql.SparqlQueryFactory;
30: import cz.cvut.kbss.jopa.sessions.change.ChangeManagerImpl;
31: import cz.cvut.kbss.jopa.sessions.change.ChangeRecordImpl;
32: import cz.cvut.kbss.jopa.sessions.change.ChangeSetFactory;
33: import cz.cvut.kbss.jopa.sessions.validator.IntegrityConstraintsValidator;
34: import cz.cvut.kbss.jopa.utils.*;
35:
36: import java.lang.reflect.Field;
37: import java.net.URI;
38: import java.util.*;
39: import java.util.Map.Entry;
40:
41: public class UnitOfWorkImpl extends AbstractSession implements UnitOfWork, QueryFactory, ConfigurationHolder, Wrapper {
42:
43: private final Map<Object, Object> cloneMapping;
44: private final Map<Object, Object> cloneToOriginals;
45: private final Map<Object, Object> keysToClones = new HashMap<>();
46: private Map<Object, Object> deletedObjects;
47: private Map<Object, Object> newObjectsCloneToOriginal;
48: private Map<Object, Object> newObjectsOriginalToClone;
49: private final Map<Object, Object> newObjectsKeyToClone = new HashMap<>();
50: private RepositoryMap repoMap;
51:
52: private boolean hasChanges;
53: private boolean hasNew;
54: private boolean hasDeleted;
55: private boolean shouldReleaseAfterCommit;
56: private boolean shouldClearCacheAfterCommit;
57: private boolean useTransactionalOntology;
58:
59: private boolean isActive;
60: private boolean inCommit;
61:
62: private UnitOfWorkChangeSet uowChangeSet;
63:
64: private AbstractSession parent;
65: private AbstractEntityManager entityManager;
66: private final ConnectionWrapper storage;
67:
68: private final MergeManager mergeManager;
69: private final CloneBuilder cloneBuilder;
70: private final ChangeManager changeManager;
71: private final QueryFactory queryFactory;
72: private final CollectionFactory collectionFactory;
73: /**
74: * This is a shortcut for the second level cache.
75: */
76: private final CacheManager cacheManager;
77:
78: public UnitOfWorkImpl(AbstractSession parent) {
79: this.parent = Objects.requireNonNull(parent, ErrorUtils.constructNPXMessage("parent"));
80: this.cloneMapping = createMap();
81: this.cloneToOriginals = createMap();
82: this.repoMap = new RepositoryMap();
83: repoMap.initDescriptors();
84: this.cloneBuilder = new CloneBuilderImpl(this);
85: this.collectionFactory = new CollectionFactory(this);
86: this.cacheManager = parent.getLiveObjectCache();
87: this.storage = acquireConnection();
88: this.queryFactory = new SparqlQueryFactory(this, storage);
89: this.mergeManager = new MergeManagerImpl(this);
90: this.changeManager = new ChangeManagerImpl(this);
91: this.inCommit = false;
92: this.useTransactionalOntology = true;
93: this.isActive = true;
94: }
95:
96: /**
97: * This method returns null, since we don't support nested Units of Work yet.
98: */
99: @Override
100: public UnitOfWork acquireUnitOfWork() {
101: return null;
102: }
103:
104: @Override
105: protected ConnectionWrapper acquireConnection() {
106: final ConnectionWrapper conn = parent.acquireConnection();
107: conn.setUnitOfWork(this);
108: return conn;
109: }
110:
111: @Override
112: public <T> T readObject(Class<T> cls, Object primaryKey, Descriptor descriptor) {
113: Objects.requireNonNull(cls, ErrorUtils.constructNPXMessage("cls"));
114: Objects.requireNonNull(primaryKey, ErrorUtils.constructNPXMessage("primaryKey"));
115: Objects.requireNonNull(descriptor, ErrorUtils.constructNPXMessage("descriptor"));
116:
117: return readObjectInternal(cls, primaryKey, descriptor);
118: }
119:
120: private <T> T readObjectInternal(Class<T> cls, Object identifier, Descriptor descriptor) {
121: assert cls != null;
122: assert identifier != null;
123: assert descriptor != null;
124: // First try to find the object among new uncommitted objects
125: Object result = newObjectsKeyToClone.get(identifier);
126: if (result != null && (isInRepository(descriptor, result))) {
127: // The result can be returned, since it is already registered in this UOW
128: return cls.cast(result);
129: }
130: // Object is already managed
131: result = keysToClones.get(identifier);
132: if (result != null) {
133: if (!cls.isAssignableFrom(result.getClass())) {
134: throw individualAlreadyManaged(identifier);
135: }
136: if (isInRepository(descriptor, result) && !getDeletedObjects().containsKey(result)) {
137: return cls.cast(result);
138: }
139: }
140: final URI idUri = EntityPropertiesUtils.getValueAsURI(identifier);
141: result = storage.find(new LoadingParameters<>(cls, idUri, descriptor));
142:
143: if (result == null) {
144: return null;
145: }
146: final Object clone = registerExistingObject(result, descriptor);
147: checkForCollections(clone);
148: return cls.cast(clone);
149: }
150:
151: private OWLEntityExistsException individualAlreadyManaged(Object identifier) {
152: return new OWLEntityExistsException(
153: "An entity with URI " + identifier + " is already present in the current persistence context.");
154: }
155:
156: /**
157: * This method calculates the changes that were to the registered entities and adds these changes into the given
158: * change set for future commit to the ontology.
159: */
160: private void calculateChanges() {
161: final UnitOfWorkChangeSet changeSet = getUowChangeSet();
162: if (hasNew) {
163: calculateNewObjects(changeSet);
164: }
165: if (hasDeleted) {
166: calculateDeletedObjects(changeSet);
167: }
168: }
169:
170: /**
171: * Create object change sets for the new objects and adds them into our UnitOfWorkChangeSet.
172: *
173: * @param changeSet UnitOfWorkChangeSet
174: */
175: private void calculateNewObjects(UnitOfWorkChangeSet changeSet) {
176: for (Object clone : getNewObjectsCloneToOriginal().keySet()) {
177: final Descriptor c = getDescriptor(clone);
178: Object original = getNewObjectsCloneToOriginal().get(clone);
179: if (original == null) {
180: original = this.cloneBuilder.buildClone(clone, c);
181: }
182: if (original == null) {
183: throw new OWLPersistenceException(
184: "Error while calculating changes for new objects. Original not found.");
185: }
186: getNewObjectsCloneToOriginal().put(clone, original);
187: getNewObjectsOriginalToClone().put(original, clone);
188: changeSet.addNewObjectChangeSet(ChangeSetFactory.createObjectChangeSet(original, clone,
189: c));
190: }
191: }
192:
193: private void calculateDeletedObjects(final UnitOfWorkChangeSet changeSet) {
194: for (Object clone : getDeletedObjects().keySet()) {
195: Object original = cloneToOriginals.get(clone);
196: if (original == null) {
197: throw new OWLPersistenceException("Cannot find an original for clone!");
198: }
199: Descriptor descriptor = getDescriptor(clone);
200: changeSet.addDeletedObjectChangeSet(ChangeSetFactory.createObjectChangeSet(original, clone,
201: descriptor));
202: }
203: }
204:
205: public void clear() {
206: detachAllManagedInstances();
207: cloneMapping.clear();
208: cloneToOriginals.clear();
209: keysToClones.clear();
210: this.deletedObjects = null;
211: this.newObjectsCloneToOriginal = null;
212: this.newObjectsOriginalToClone = null;
213: this.newObjectsKeyToClone.clear();
214: this.hasChanges = false;
215: this.hasDeleted = false;
216: this.hasNew = false;
217: }
218:
219: private void detachAllManagedInstances() {
220: cloneMapping.keySet().forEach(this::unregisterObjectFromPersistenceContext);
221: }
222:
223: public boolean contains(Object entity) {
224: Objects.requireNonNull(entity, ErrorUtils.constructNPXMessage("entity"));
225:
226: return isObjectManaged(entity);
227: }
228:
229: public void commit() {
230: LOG.trace("UnitOfWork commit started.");
231: if (!isActive()) {
232: throw new IllegalStateException("Cannot commit inactive Unit of Work!");
233: }
234: this.inCommit = true;
235: commitUnitOfWork();
236: LOG.trace("UnitOfWork commit finished.");
237: }
238:
239: public void rollback() {
240: LOG.trace("UnitOfWork rollback started.");
241: if (!isActive()) {
242: throw new IllegalStateException("Cannot rollback inactive Unit of Work!");
243: }
244: storage.rollback();
245: clear();
246: }
247:
248: /**
249: * Commit this Unit of Work.
250: */
251: private void commitUnitOfWork() {
252: commitToOntology();
253: mergeChangesIntoParent();
254: postCommit();
255: }
256:
257: /**
258: * Clean up after the commit.
259: */
260: private void postCommit() {
261: // Remove indirect collections from clones
262: cloneMapping.keySet().forEach(this::removeIndirectCollections);
263: getNewObjectsCloneToOriginal().clear();
264: getNewObjectsOriginalToClone().clear();
265: newObjectsKeyToClone.clear();
266: getDeletedObjects().clear();
267: cloneToOriginals.clear();
268: cloneMapping.clear();
269: keysToClones.clear();
270: this.hasChanges = false;
271: this.hasDeleted = false;
272: this.hasNew = false;
273: this.inCommit = false;
274: cloneBuilder.reset();
275: this.repoMap = new RepositoryMap();
276: repoMap.initDescriptors();
277: this.uowChangeSet = null;
278: if (shouldClearCacheAfterCommit) {
279: cacheManager.evictAll();
280: this.shouldReleaseAfterCommit = true;
281: }
282: }
283:
284: /**
285: * If there are any changes, commit them to the ontology.
286: */
287: private void commitToOntology() {
288: boolean hasChanges = this.hasNew || this.hasChanges || this.hasDeleted;
289: if (hasChanges) {
290: calculateChanges();
291: }
292: validateIntegrityConstraints();
293: storageCommit();
294: }
295:
296: private void validateIntegrityConstraints() {
297: if (uowChangeSet == null) {
298: return;
299: }
300: final IntegrityConstraintsValidator validator = IntegrityConstraintsValidator.getValidator();
301: for (ObjectChangeSet changeSet : uowChangeSet.getNewObjects()) {
302: validator.validate(changeSet.getCloneObject(),
303: getMetamodel().entity((Class<Object>) changeSet.getObjectClass()), false);
304: }
305: uowChangeSet.getExistingObjectsChanges().forEach(changeSet -> validator.validate(changeSet, getMetamodel()));
306: }
307:
308: private Map<Object, Object> createMap() {
309: return new IdentityHashMap<>();
310: }
311:
312: /**
313: * Gets current state of the specified entity. </p>
314: * <p>
315: * Note that since no repository is specified we can only determine if the entity is managed or removed. Therefore
316: * if the case is different this method returns State#NOT_MANAGED.
317: *
318: * @param entity The entity to check
319: * @return State of the entity
320: */
321: public State getState(Object entity) {
322: Objects.requireNonNull(entity, ErrorUtils.constructNPXMessage("entity"));
323:
324: if (getDeletedObjects().containsKey(entity)) {
325: return State.REMOVED;
326: } else if (getNewObjectsCloneToOriginal().containsKey(entity)) {
327: return State.MANAGED_NEW;
328: } else if (cloneMapping.containsKey(entity)) {
329: return State.MANAGED;
330: } else {
331: return State.NOT_MANAGED;
332: }
333: }
334:
335: /**
336: * Checks the state of the specified entity with regards to the specified repository.
337: *
338: * @param entity Object
339: * @param descriptor Entity descriptor
340: * @return The state of the specified entity
341: */
342: public State getState(Object entity, Descriptor descriptor) {
343: Objects.requireNonNull(entity, ErrorUtils.constructNPXMessage("entity"));
344: Objects.requireNonNull(descriptor, ErrorUtils.constructNPXMessage("descriptor"));
345:
346: if (getDeletedObjects().containsKey(entity)) {
347: return State.REMOVED;
348: } else if (cloneMapping.containsKey(entity) && isInRepository(descriptor, entity)) {
349: if (getNewObjectsCloneToOriginal().containsKey(entity)) {
350: return State.MANAGED_NEW;
351: }
352: return State.MANAGED;
353: } else {
354: return State.NOT_MANAGED;
355: }
356: }
357:
358: /**
359: * Tries to find the original object for the given clone. It searches the existing objects, new objects and deleted
360: * objects.
361: *
362: * @param clone Object
363: * @return The original object for the given clone
364: */
365: public Object getOriginal(Object clone) {
366: if (clone == null) {
367: return null;
368: }
369: Object original = cloneToOriginals.get(clone);
370: if (original == null) {
371: original = getNewObjectsCloneToOriginal().get(clone);
372: }
373: return original;
374: }
375:
376: /**
377: * Gets managed original with the specified identifier or {@code null} if there is none matching.
378: * <p>
379: * Descriptor is used to check repository context validity.
380: *
381: * @param cls Return type of the original
382: * @param identifier Instance identifier
383: * @param descriptor Repository descriptor
384: * @return Original object managed by this UoW or {@code null} if this UoW doesn't contain a matching instance
385: */
386: public <T> T getManagedOriginal(Class<T> cls, Object identifier, Descriptor descriptor) {
387: if (!keysToClones.containsKey(identifier)) {
388: return null;
389: }
390: final Object clone = keysToClones.get(identifier);
391: if (!cls.isAssignableFrom(clone.getClass())) {
392: return null;
393: }
394: if (!isInRepository(descriptor, clone)) {
395: return null;
396: }
397: return cls.cast(cloneToOriginals.get(clone));
398: }
399:
400: /**
401: * Check if this UnitOfWork contains this original entity. This method is used by the CloneBuilder so it does not
402: * have to clone already managed referenced objects.
403: *
404: * @param entity The original entity.
405: * @return True if the original is managed in this UnitOfWork.
406: */
407: boolean containsOriginal(Object entity) {
408: return entity != null && cloneToOriginals.containsValue(entity);
409: }
410:
411: /**
412: * Finds clone for the specified original. This method assumes that the original is managed in this persistence
413: * context (UnitOfWork). However, if not, this method just goes through all the managed objects and if it does not
414: * find match, returns null.
415: *
416: * @param original The original object whose clone we are looking for.
417: * @return The clone or null, if there is none.
418: */
419: Object getCloneForOriginal(Object original) {
420: for (Entry<Object, Object> entry : cloneToOriginals.entrySet()) {
421: // We use IdentityMap, so we can use ==
422: if (entry.getValue() == original) {
423: return entry.getKey();
424: }
425: }
426: return null;
427: }
428:
429: public boolean hasChanges() {
430: return hasChanges || hasDeleted || hasNew;
431: }
432:
433: void setHasChanges() {
434: this.hasChanges = true;
435: }
436:
437: Map<Object, Object> getDeletedObjects() {
438: if (deletedObjects == null) {
439: this.deletedObjects = createMap();
440: }
441: return deletedObjects;
442: }
443:
444: Map<Object, Object> getNewObjectsCloneToOriginal() {
445: if (newObjectsCloneToOriginal == null) {
446: this.newObjectsCloneToOriginal = createMap();
447: }
448: return newObjectsCloneToOriginal;
449: }
450:
451: private Map<Object, Object> getNewObjectsOriginalToClone() {
452: if (newObjectsOriginalToClone == null) {
453: this.newObjectsOriginalToClone = createMap();
454: }
455: return newObjectsOriginalToClone;
456: }
457:
458: @Override
459: public CacheManager getLiveObjectCache() {
460: return parent.getLiveObjectCache();
461: }
462:
463: public UnitOfWorkChangeSet getUowChangeSet() {
464: if (uowChangeSet == null) {
465: this.uowChangeSet = ChangeSetFactory.createUoWChangeSet();
466: }
467: return uowChangeSet;
468: }
469:
470: public boolean isActive() {
471: return this.isActive;
472: }
473:
474: /**
475: * Returns true if the given clone represents a newly created object. Otherwise returns false.
476: *
477: * @param clone Object
478: * @return boolean
479: */
480: public boolean isObjectNew(Object clone) {
481: return clone != null && getNewObjectsCloneToOriginal().containsKey(clone);
482: }
483:
484: /**
485: * Returns true if the given object is already managed.
486: *
487: * @param entity Object
488: * @return boolean
489: */
490: public boolean isObjectManaged(Object entity) {
491: Objects.requireNonNull(entity, ErrorUtils.constructNPXMessage("entity"));
492:
493: return (cloneMapping.containsKey(entity) && !getDeletedObjects().containsKey(entity));
494: }
495:
496: /**
497: * Persists changed value of the specified field.
498: *
499: * @param entity Entity with changes (the clone)
500: * @param f The field whose value has changed
501: * @throws IllegalStateException If this UoW is not in transaction
502: */
503: public void attributeChanged(Object entity, Field f) {
504: if (!isInTransaction()) {
505: throw new IllegalStateException("This unit of work is not in a transaction.");
506: }
507: final Descriptor descriptor = getDescriptor(entity);
508: if (descriptor == null) {
509: throw new OWLPersistenceException("Unable to find repository for entity " + entity
510: + ". Is it registered in this UoW?");
511: }
512: storage.merge(entity, f, descriptor);
513: createChangeRecord(entity, f, descriptor);
514: setHasChanges();
515: setIndirectCollectionIfPresent(entity, f);
516: }
517:
518: private void createChangeRecord(Object clone, Field field, Descriptor descriptor) {
519: final Object orig = getOriginal(clone);
520: if (orig == null) {
521: return;
522: }
523: final ChangeRecord record = new ChangeRecordImpl(field.getName(),
524: EntityPropertiesUtils.getFieldValue(field, clone));
525: registerChangeRecord(clone, orig, descriptor, record);
526: }
527:
528: private void registerChangeRecord(Object clone, Object orig, Descriptor descriptor, ChangeRecord record) {
529: ObjectChangeSet chSet = getUowChangeSet().getExistingObjectChanges(orig);
530: if (chSet == null) {
531: chSet = ChangeSetFactory.createObjectChangeSet(orig, clone, descriptor);
532: getUowChangeSet().addObjectChangeSet(chSet);
533: }
534: chSet.addChangeRecord(record);
535: }
536:
537: /**
538: * Merge the changes from this Unit of Work's change set into the server session.
539: */
540: public void mergeChangesIntoParent() {
541: if (hasChanges()) {
542: mergeManager.mergeChangesFromChangeSet(getUowChangeSet());
543: }
544: }
545:
546: @Override
547: public <T> T mergeDetached(T entity, Descriptor descriptor) {
548: Objects.requireNonNull(entity, ErrorUtils.constructNPXMessage("entity"));
549: Objects.requireNonNull(descriptor, ErrorUtils.constructNPXMessage("descriptor"));
550:
551: final Object id = getIdentifier(entity);
552: if (!storage.contains(id, entity.getClass(), descriptor)) {
553: registerNewObject(entity, descriptor);
554: return entity;
555: } else {
556: if (isIndividualManaged(id, entity) && !isSameType(id, entity)) {
557: throw individualAlreadyManaged(id);
558: }
559: return mergeDetachedInternal(entity, descriptor);
560: }
561: }
562:
563: private boolean isSameType(Object id, Object entity) {
564: final Class<?> mergedType = entity.getClass();
565: final Object managed = keysToClones.containsKey(id) ? keysToClones.get(id) : newObjectsKeyToClone.get(id);
566: return managed != null && managed.getClass().isAssignableFrom(mergedType);
567: }
568:
569: private <T> T mergeDetachedInternal(T entity, Descriptor descriptor) {
570: assert entity != null;
571: final Object iri = getIdentifier(entity);
572: final Class<T> entityCls = (Class<T>) entity.getClass();
573: final URI idUri = EntityPropertiesUtils.getValueAsURI(iri);
574: T original = storage.find(new LoadingParameters<>(entityCls, idUri, descriptor, true));
575:
576: assert original != null;
577: registerClone(entity, original, descriptor);
578: try {
579: // Merge only the changed attributes
580: final ObjectChangeSet chSet = ChangeSetFactory.createObjectChangeSet(original, entity, descriptor);
581: changeManager.calculateChanges(chSet);
582: final EntityType<?> et = getMetamodel().entity(entityCls);
583: for (ChangeRecord record : chSet.getChanges().values()) {
584: final Field field = et.getFieldSpecification(record.getAttributeName()).getJavaField();
585: storage.merge(entity, field, descriptor);
586: }
587: getUowChangeSet().addObjectChangeSet(chSet);
588: } catch (OWLEntityExistsException e) {
589: unregisterObject(entity);
590: throw e;
591: } catch (IllegalAccessException e) {
592: throw new OWLPersistenceException(e);
593: }
594: if (cacheManager.contains(entityCls, iri, descriptor.getContext())) {
595: cacheManager.evict(entityCls, iri, descriptor.getContext());
596: }
597: setHasChanges();
598: return entity;
599: }
600:
601: /**
602: * {@inheritDoc}
603: */
604: @Override
605: void registerEntityWithPersistenceContext(Object entity, UnitOfWorkImpl uow) {
606: parent.registerEntityWithPersistenceContext(entity, uow);
607: }
608:
609: @Override
610: void deregisterEntityFromPersistenceContext(Object entity, UnitOfWork uow) {
611: parent.deregisterEntityFromPersistenceContext(entity, uow);
612: }
613:
614: @Override
615: void releasePersistenceContext(UnitOfWork uow) {
616: parent.releasePersistenceContext(uow);
617: }
618:
619: @Override
620: public NamedQueryManager getNamedQueryManager() {
621: return parent.getNamedQueryManager();
622: }
623:
624: @Override
625: public Object registerExistingObject(Object entity, Descriptor descriptor) {
626: if (entity == null) {
627: return null;
628: }
629: if (cloneToOriginals.containsValue(entity)) {
630: return getCloneForOriginal(entity);
631: }
632: Object clone = this.cloneBuilder.buildClone(entity, descriptor);
633: assert clone != null;
634: registerClone(clone, entity, descriptor);
635: return clone;
636: }
637:
638: private void registerClone(Object clone, Object original, Descriptor descriptor) {
639: cloneMapping.put(clone, clone);
640: cloneToOriginals.put(clone, original);
641: final Object identifier = EntityPropertiesUtils.getPrimaryKey(clone, getMetamodel());
642: keysToClones.put(identifier, clone);
643: registerEntityWithPersistenceContext(clone, this);
644: registerEntityWithOntologyContext(descriptor, clone);
645: }
646:
647: /**
648: * Release this Unit of Work. Releasing an active Unit of Work with uncommitted changes causes all pending changes
649: * to be discarded.
650: */
651: public void release() {
652: clear();
653: storage.close();
654: releasePersistenceContext(this);
655: this.isActive = false;
656: LOG.debug("UnitOfWork released.");
657: }
658:
659: @Override
660: public <T> void revertObject(T object) {
661: Objects.requireNonNull(object, ErrorUtils.constructNPXMessage("object"));
662:
663: if (!isObjectManaged(object) && !getDeletedObjects().containsKey(object)) {
664: throw new IllegalArgumentException(
665: "The specified entity " + object + " is not managed by this persistence context.");
666: }
667: final Descriptor descriptor = getDescriptor(object);
668: if (descriptor == null) {
669: throw new IllegalArgumentException("Unable to find entity " + object + " in this persistence context.");
670: }
671: // To revert the object's state, just swap original and clone for change
672: // calculation and merging so that the state of the original is merged
673: // into the state of the clone
674: final Object original = getOriginal(object);
675: final ObjectChangeSet chSet = ChangeSetFactory.createObjectChangeSet(object, original,
676: descriptor);
677: try {
678: final boolean anyChanges = changeManager.calculateChanges(chSet);
679: if (anyChanges) {
680: mergeManager.mergeChangesOnObject(original, chSet);
681: }
682: } catch (IllegalAccessException | IllegalArgumentException e) {
683: throw new OWLPersistenceException(e);
684: }
685: }
686:
687: @Override
688: public void registerNewObject(Object entity, Descriptor descriptor) {
689: Objects.requireNonNull(entity, ErrorUtils.constructNPXMessage("entity"));
690: Objects.requireNonNull(descriptor, ErrorUtils.constructNPXMessage("descriptor"));
691:
692: registerNewObjectInternal(entity, descriptor);
693: }
694:
695: /**
696: * Registers the specified entity for persist in this Unit of Work.
697: *
698: * @param entity The entity to register
699: * @param descriptor Entity descriptor, specifying optionally contexts into which the entity will be persisted
700: */
701: private void registerNewObjectInternal(Object entity, Descriptor descriptor) {
702: assert entity != null;
703: Object id = getIdentifier(entity);
704: if (id == null) {
705: final EntityType<?> eType = getMetamodel().entity(entity.getClass());
706: EntityPropertiesUtils.verifyIdentifierIsGenerated(entity, eType);
707: }
708: if (isIndividualManaged(id, entity) && !entity.getClass().isEnum()) {
709: throw individualAlreadyManaged(id);
710: }
711: storage.persist(id, entity, descriptor);
712: if (id == null) {
713: // If the ID was null, extract it from the entity
714: // It is present now
715: id = getIdentifier(entity);
716: }
717: // Original is null until commit
718: cloneMapping.put(entity, entity);
719: getNewObjectsCloneToOriginal().put(entity, null);
720: registerEntityWithPersistenceContext(entity, this);
721: registerEntityWithOntologyContext(descriptor, entity);
722: newObjectsKeyToClone.put(id, entity);
723: checkForCollections(entity);
724: this.hasNew = true;
725: }
726:
727: private boolean isIndividualManaged(Object identifier, Object entity) {
728: return keysToClones.containsKey(identifier) ||
729: newObjectsKeyToClone.containsKey(identifier) && !cloneMapping.containsKey(entity);
730: }
731:
732: /**
733: * Remove the specified entity from the ontology.
734: *
735: * @param entity Managed entity to delete
736: */
737: public void removeObject(Object entity) {
738: assert entity != null;
739: if (!isObjectManaged(entity)) {
740: throw new IllegalArgumentException(
741: "Cannot remove entity which is not managed in the current persistence context.");
742: }
743: final Object primaryKey = getIdentifier(entity);
744: final Descriptor descriptor = getDescriptor(entity);
745:
746: if (hasNew && getNewObjectsCloneToOriginal().containsKey(entity)) {
747: unregisterObject(entity);
748: newObjectsKeyToClone.remove(primaryKey);
749: } else {
750: getDeletedObjects().put(entity, entity);
751: this.hasDeleted = true;
752: }
753: storage.remove(primaryKey, entity.getClass(), descriptor);
754: }
755:
756: /**
757: * Remove the registered object from this Unit of Work.
758: *
759: * @param object Clone of the original object
760: */
761: public void unregisterObject(Object object) {
762: if (object == null) {
763: return;
764: }
765: cloneMapping.remove(object);
766: cloneToOriginals.remove(object);
767:
768: getDeletedObjects().remove(object);
769: if (hasNew) {
770: Object newOriginal = getNewObjectsCloneToOriginal().remove(object);
771: if (newOriginal != null) {
772: getNewObjectsOriginalToClone().remove(newOriginal);
773: }
774: }
775: unregisterObjectFromPersistenceContext(object);
776: }
777:
778: private void unregisterObjectFromPersistenceContext(Object object) {
779: removeIndirectCollections(object);
780: deregisterEntityFromPersistenceContext(object, this);
781: unregisterEntityFromOntologyContext(object);
782: }
783:
784: public boolean shouldReleaseAfterCommit() {
785: return shouldReleaseAfterCommit;
786: }
787:
788: public void setShouldClearAfterCommit(boolean shouldClearCache) {
789: this.shouldClearCacheAfterCommit = shouldClearCache;
790: }
791:
792: public void setEntityManager(AbstractEntityManager entityManager) {
793: this.entityManager = entityManager;
794: }
795:
796: public void writeUncommittedChanges() {
797: if (!hasChanges()) {
798: return;
799: }
800: commitUnitOfWork();
801: }
802:
803: @Override
804: public MetamodelImpl getMetamodel() {
805: return parent.getMetamodel();
806: }
807:
808: @Override
809: public boolean isTypeManaged(Class<?> cls) {
810: return parent.isTypeManaged(cls);
811: }
812:
813: @Override
814: public boolean isInTransaction() {
815: return entityManager != null && entityManager.getTransaction().isActive();
816: }
817:
818: /**
819: * Returns {@code true} if this UoW is currently committing changes.
820: */
821: public boolean isInCommit() {
822: return inCommit;
823: }
824:
825: @Override
826: public <T> void loadEntityField(T entity, Field field) {
827: Objects.requireNonNull(entity, ErrorUtils.constructNPXMessage("entity"));
828: Objects.requireNonNull(field, ErrorUtils.constructNPXMessage("field"));
829:
830: if (EntityPropertiesUtils.getFieldValue(field, entity) != null) {
831: return;
832: }
833: final Descriptor entityDescriptor = getDescriptor(entity);
834: if (entityDescriptor == null) {
835: throw new OWLPersistenceException(
836: "Unable to find repository identifier for entity " + entity
837: + ". Is it managed by this UoW?");
838: }
839: storage.loadFieldValue(entity, field, entityDescriptor);
840: final Object orig = EntityPropertiesUtils.getFieldValue(field, entity);
841: final Object entityOriginal = getOriginal(entity);
842: if (entityOriginal != null) {
843: EntityPropertiesUtils.setFieldValue(field, entityOriginal, orig);
844: }
845: final Descriptor fieldDescriptor = getFieldDescriptor(entity, field, entityDescriptor);
846: final Object clone = cloneLoadedFieldValue(entity, field, fieldDescriptor, orig);
847: EntityPropertiesUtils.setFieldValue(field, entity, clone);
848: }
849:
850: private <T> Descriptor getFieldDescriptor(T entity, Field field, Descriptor entityDescriptor) {
851: final EntityType<?> et = getMetamodel().entity(entity.getClass());
852: final FieldSpecification<?, ?> fieldSpec = et
853: .getFieldSpecification(field.getName());
854: return entityDescriptor.getAttributeDescriptor(fieldSpec);
855: }
856:
857: private <T> Object cloneLoadedFieldValue(T entity, Field field, final Descriptor fieldDescriptor,
858: final Object fieldValueOrig) {
859: Object clone;
860: if (fieldValueOrig == null) {
861: clone = null;
862: } else {
863: if (isTypeManaged(field.getType())) {
864: clone = registerExistingObject(fieldValueOrig, fieldDescriptor);
865: final URI fieldContext = fieldDescriptor.getContext();
866: putObjectIntoCache(getIdentifier(clone), fieldValueOrig, fieldContext);
867: } else {
868: clone = cloneBuilder.buildClone(entity, field, fieldValueOrig, fieldDescriptor);
869: }
870: }
871: return clone;
872: }
873:
874: @Override
875: public void removeObjectFromCache(Object toRemove, URI context) {
876: Objects.requireNonNull(toRemove, ErrorUtils.constructNPXMessage("toRemove"));
877:
878: final Object primaryKey = getIdentifier(toRemove);
879: cacheManager.evict(toRemove.getClass(), primaryKey, context);
880: }
881:
882: @Override
883: public boolean isConsistent(URI context) {
884: return storage.isConsistent(context);
885: }
886:
887: @Override
888: public List<URI> getContexts() {
889: return storage.getContexts();
890: }
891:
892: @Override
893: public void setUseTransactionalOntologyForQueryProcessing() {
894: this.useTransactionalOntology = true;
895: }
896:
897: @Override
898: public boolean useTransactionalOntologyForQueryProcessing() {
899: return useTransactionalOntology;
900: }
901:
902: @Override
903: public void setUseBackupOntologyForQueryProcessing() {
904: this.useTransactionalOntology = false;
905: }
906:
907: @Override
908: public boolean useBackupOntologyForQueryProcessing() {
909: return !useTransactionalOntology;
910: }
911:
912: @Override
913: public Query createNativeQuery(String sparql) {
914: return queryFactory.createNativeQuery(sparql);
915: }
916:
917: @Override
918: public <T> TypedQuery<T> createNativeQuery(String sparql, Class<T> resultClass) {
919: return queryFactory.createNativeQuery(sparql, resultClass);
920: }
921:
922: @Override
923: public Query createQuery(String query) {
924: return queryFactory.createQuery(query);
925: }
926:
927: @Override
928: public <T> TypedQuery<T> createQuery(String query, Class<T> resultClass) {
929: return queryFactory.createQuery(query, resultClass);
930: }
931:
932: @Override
933: public Query createNamedQuery(String name) {
934: return queryFactory.createNamedQuery(name);
935: }
936:
937: @Override
938: public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
939: return queryFactory.createNamedQuery(name, resultClass);
940: }
941:
942: /**
943: * Check if the specified entity contains a collection. If so, replace it with its indirect representation so that
944: * changes in that collection can be tracked.
945: *
946: * @param entity The entity to check
947: */
948: private void checkForCollections(Object entity) {
949: // TODO This should iterate over all mapped fields, not just the declared ones
950: Field[] fields = entity.getClass().getDeclaredFields();
951: for (Field f : fields) {
952: setIndirectCollectionIfPresent(entity, f);
953: }
954: }
955:
956: /**
957: * Create and set indirect collection on the specified entity field.</p>
958: * <p>
959: * If the specified field is of Collection type and it is not already an indirect collection, create new one and set
960: * it as the value of the specified field on the specified entity.
961: *
962: * @param entity The entity collection will be set on
963: * @param field The field to set
964: * @throws IllegalArgumentException Reflection
965: */
966: public void setIndirectCollectionIfPresent(Object entity, Field field) {
967: Objects.requireNonNull(entity, ErrorUtils.constructNPXMessage("entity"));
968: Objects.requireNonNull(field, ErrorUtils.constructNPXMessage("field"));
969:
970: Object value = EntityPropertiesUtils.getFieldValue(field, entity);
971: if (value == null || value instanceof IndirectCollection) {
972: return;
973: }
974: if (value instanceof Collection || value instanceof Map) {
975: EntityPropertiesUtils.setFieldValue(field, entity, createIndirectCollection(value, entity, field));
976: }
977: }
978:
979: /**
980: * Creates an indirect collection, which wraps the specified collection instance and propagates changes to the
981: * persistence context.
982: *
983: * @param collection Collection to be proxied
984: * @param owner Collection owner instance
985: * @param field Field filled with the collection
986: * @return Indirect collection
987: */
988: public IndirectCollection<?> createIndirectCollection(Object collection, Object owner, Field field) {
989: return collectionFactory.createIndirectCollection(collection, owner, field);
990: }
991:
992: /**
993: * Remove indirect collection implementations from the specified entity (if present).
994: *
995: * @param entity The entity to remove indirect collections from
996: */
997: private void removeIndirectCollections(Object entity) {
998: Field[] fields = entity.getClass().getDeclaredFields();
999: for (Field f : fields) {
1000: final Object ob = EntityPropertiesUtils.getFieldValue(f, entity);
1001: if (ob == null) {
1002: continue;
1003: }
1004: if (ob instanceof IndirectCollection) {
1005: IndirectCollection<?> indCol = (IndirectCollection<?>) ob;
1006: EntityPropertiesUtils.setFieldValue(f, entity, indCol.getReferencedCollection());
1007: }
1008: }
1009: }
1010:
1011: void putObjectIntoCache(Object primaryKey, Object entity, URI context) {
1012: cacheManager.add(primaryKey, entity, context);
1013: }
1014:
1015: private Object getIdentifier(Object entity) {
1016: assert entity != null;
1017: return EntityPropertiesUtils.getPrimaryKey(entity, getMetamodel());
1018: }
1019:
1020: private void unregisterEntityFromOntologyContext(Object entity) {
1021: assert entity != null;
1022:
1023: final Descriptor descriptor = repoMap.getEntityDescriptor(entity);
1024: if (descriptor == null) {
1025: throw new OWLPersistenceException("Fatal error, unable to find descriptor for entity " + entity);
1026: }
1027:
1028: repoMap.remove(descriptor, entity);
1029: repoMap.removeEntityToRepository(entity);
1030: }
1031:
1032: private void registerEntityWithOntologyContext(Descriptor repository, Object entity) {
1033: assert repository != null;
1034: assert entity != null;
1035:
1036: repoMap.add(repository, entity, null);
1037: repoMap.addEntityToRepository(entity, repository);
1038: }
1039:
1040: private boolean isInRepository(Descriptor descriptor, Object entity) {
1041: assert descriptor != null;
1042: assert entity != null;
1043:
1044: return repoMap.contains(descriptor, entity);
1045: }
1046:
1047: private Descriptor getDescriptor(Object entity) {
1048: assert entity != null;
1049:
1050: return repoMap.getEntityDescriptor(entity);
1051: }
1052:
1053: private void storageCommit() {
1054: try {
1055: storage.commit();
1056: } catch (OWLPersistenceException e) {
1057: entityManager.removeCurrentPersistenceContext();
1058: throw e;
1059: }
1060: }
1061:
1062: @Override
1063: public Configuration getConfiguration() {
1064: return entityManager.getConfiguration();
1065: }
1066:
1067: @Override
1068: public <T> T unwrap(Class<T> cls) {
1069: if (cls.isAssignableFrom(getClass())) {
1070: return cls.cast(this);
1071: }
1072: return storage.unwrap(cls);
1073: }
1074: }